<?php defined('SYSPATH') or die('No direct script access.');
/**
 * Session 命名空间
 *
 * @author  Yellow.Chow <aultoale@gmail.com>
 * @version $Id: namespace.php 43 2012-03-09 08:24:10Z yellow.chow $
 * @license http://www.gnu.org/licenses/gpl-3.0.html    GPL 3
 */
class Session_NameSpace implements IteratorAggregate
{

    /**
     * 命名空间工厂实例
     *
     * @var array
     */
    protected static $_instances = array();

    /**
     * 工厂方法获取命名空间实例
     *
     * @param  string  $namespace
     * @return Session_NameSpace
     */
    public static function factory($namespace)
    {
        if ( ! isset(self::$_instances[$namespace]))
        {
            self::$_instances[$namespace] = new Session_NameSpace($namespace);
        }

        return self::$_instances[$namespace];
    }

    /**
     * 空间名称
     *
     * @var string
     */
    protected $_namespace = NULL;

    /**
     * Session NameSpace data
     *
     * @var array
     */
    protected $_data = array();

    /**
     * 构造方法
     *
     * @param  string  $namespace
     * @throws Session_Exception
     */
    protected function __construct($namespace)
    {
        /**
         * 空间名称
         */
        if ( ! preg_match('#^[\w\-]+$#', $namespace))
        {
            throw new Session_Exception('无效的 Session 命令空间 :namespace',
                array(':namespace' => $namespace));
        }
        $this->_namespace = strtoupper($namespace);

        /**
         * 加载并引用数据
         */
        $data = & Session::instance()->as_array();
        if ( ! isset($data['__NS'][$this->_namespace]))
        {
            $data['__NS'][$this->_namespace] = array();
        }
        $this->_data = & $data['__NS'][$this->_namespace];

        /**
         * 时间过期
         */
        if (isset($this->_data['__time']) AND $this->_data['__time'] < time())
        {
            $this->clean();
        }

        /**
         * 使用次数过期
         */
        if (isset($this->_data['__hops']))
        {
            $this->_data['__hops']--;
            if ($this->_data['__hops'] == 0)
            {
                $this->clean();
            }
        }
    }

    /**
     * 设置数据
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return Session_NameSpace
     * @throws Session_Exception
     */
    public function set($key, $value = NULL)
    {
        if (is_array($key))
        {
            foreach ($key as $k => $v)
            {
                $this->set($k, $v);
            }
        }
        elseif (substr($key, 0, 2) == '__')
        {
            throw new Session_Exception('禁止修改以下划线 __ 开头的数据');
        }
        else
        {
            $this->_data[$key] = $value;
        }

        return $this;
    }

    /**
     * 魔术方法 - 设置数据
     *
     * @param  string  $key
     * @param  mixed   $value
     */
    public function __set($key, $value)
    {
        $this->set($key, $value);
    }

    /**
     * 获取数据
     *
     * @param  string  $key
     * @param  mixed   $default
     * @return mixed
     */
    public function get($key, $default = NULL)
    {
        return isset($this->_data[$key]) ? $this->_data[$key] : $default;
    }

    /**
     * 魔术方法 - 获取数据
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        return $this->get($key);
    }

    /**
     * 删除数据
     *
     * @param  string  $key
     * @return Session_NameSpace
     * @throws Exception
     */
    public function del($key)
    {
        unset($this->_data[$key]);

        return $this;
    }

    /**
     * 魔术方法 - 删除数据
     *
     * @param  string  $key
     */
    public function __unset($key)
    {
        $this->del($key);
    }

    /**
     * 是否存在数据
     *
     * @param  string  $key
     * @return bool
     */
    public function has($key)
    {
        return isset($this->_data[$key]);
    }

    /**
     * 魔术方法 - 是否存在数据
     *
     * @param  string  $key
     * @return bool
     */
    public function __isset($key)
    {
        return $this->has($key);
    }

    /**
     * 是否为空
     *
     * @return bool
     */
    public function is_empty()
    {
        foreach ($this->_data as $key => $value)
        {
            if (substr($key, 0, 2) != '__')
            {
                return FALSE;
            }
        }

        return TRUE;
    }

    /**
     * 清空命名空间
     *
     * @return Session_NameSpace
     */
    public function clean()
    {
        $this->_data = array();

        return $this;
    }

    /**
     * 获取所有数据
     *
     * @return array
     */
    public function as_array()
    {
        $data = $this->_data;
        foreach ($this->_data as $key => $value)
        {
            if (substr($key, 0, 2) == '__')
            {
                unset($data[$key]);
            }
        }

        return $data;
    }

    /**
     * 取出所有数据 - 能够用于 foreach
     *
     * @return ArrayObject
     */
    public function getIterator()
    {
        return new ArrayObject($this->as_array());
    }

    /**
     * 设置过期时间(秒)
     *
     * @param  int  $seconds
     * @return Session_NameSpace
     * @throws Session_Exception
     */
    public function lifetime($seconds)
    {
        if ($seconds <= 0)
        {
            throw new Session_Exception("Session 命名空间过期时间必须大于 '0'");
        }

        $this->_data['__time'] = time() + (int) $seconds;

        return $this;
    }

    /**
     * 设置在使用 $hops 次后过期
     *
     * @param  int  $hops
     * @return Session_NameSpace
     * @throws Session_Exception
     */
    public function hops($hops)
    {
        if ($hops <= 0)
        {
            throw new Session_Exception("Session 命名空间使用次数必须大于 '0'");
        }

        $this->_data['__hops'] = (int) $hops;

        return $this;
    }

    /**
     * 输出命名空间为字符串
     *
     * @return string
     */
    public function __toString()
    {
        return $this->_namespace;
    }
}